package com.zackeus.xupdate;

import android.app.Application;
import android.content.Context;
import android.util.SparseArray;

import androidx.annotation.NonNull;

import com.elvishew.xlog.XLog;
import com.zackeus.common.utils.FileUtils;
import com.zackeus.common.utils.SPUtils;
import com.zackeus.xupdate.constant.Error;
import com.zackeus.xupdate.entity.LibraryUpdateEntity;
import com.zackeus.xupdate.entity.UpdateError;
import com.zackeus.xupdate.factory.IFileEncryptor;
import com.zackeus.xupdate.factory.impl.DefaultFileEncryptor;
import com.zackeus.xupdate.factory.IUpdateInstaller;
import com.zackeus.xupdate.listener.OnUpdateFailureListener;
import com.zackeus.xupdate.factory.impl.DefaultIUpdateInstaller;
import com.zackeus.xupdate.listener.impl.DefaultUpdateFailureListener;

import java.io.File;
import java.util.Map;
import java.util.TreeMap;

/**
 * 版本更新
 * @name: XUpdateManager
 * @author: zz
 * @date: 2022/11/17
 */
public class XUpdate {

    public static final String LOG_TAG = XUpdate.class.getSimpleName();
    private static final String SP_NAME = "XUPDATE";
    private static final String IGNORE_VERSION = "xupdate_ignore_version";

    static volatile XUpdate mInstance;
    private final Application context;

    private final SparseArray<String> eMessages = new SparseArray<>();

    //========全局属性==========//

    /* 请求参数【比如apk-key或者versionCode等】 */
    private final Map<String, Object> mParams;

    /* 文件加密器 */
    private final IFileEncryptor mFileEncryptor;

    /* APK安装监听 */
    private final IUpdateInstaller mInstaller;
    /* 更新出错监听 */
    private final OnUpdateFailureListener mOnUpdateFailureListener;

    private XUpdate(@NonNull final Application context, @NonNull Map<String, Object> params,
                    @NonNull IFileEncryptor fileEncryptor,
                    @NonNull IUpdateInstaller installer, @NonNull OnUpdateFailureListener failureListener) {
        this.context = context;
        this.mParams = params;

        this.mFileEncryptor = fileEncryptor;

        this.mInstaller = installer;
        this.mOnUpdateFailureListener = failureListener;

        initError();
    }

    /**
     * 异常信息初始化
     */
    private void initError() {
        this.eMessages.append(Error.CHECK_NET_REQUEST, this.context.getString(R.string.xupdate_error_check_net_request));
        this.eMessages.append(Error.CHECK_NO_WIFI, this.context.getString(R.string.xupdate_error_check_no_wifi));
        this.eMessages.append(Error.CHECK_NO_NETWORK, this.context.getString(R.string.xupdate_error_check_no_network));
        this.eMessages.append(Error.CHECK_UPDATING, this.context.getString(R.string.xupdate_error_check_updating));
        this.eMessages.append(Error.CHECK_NO_NEW_VERSION, this.context.getString(R.string.xupdate_error_check_no_new_version));
        this.eMessages.append(Error.CHECK_JSON_EMPTY, this.context.getString(R.string.xupdate_error_check_json_empty));
        this.eMessages.append(Error.CHECK_PARSE, this.context.getString(R.string.xupdate_error_check_parse));
        this.eMessages.append(Error.CHECK_IGNORED_VERSION, this.context.getString(R.string.xupdate_error_check_ignored_version));
        this.eMessages.append(Error.CHECK_APK_CACHE_DIR_EMPTY, this.context.getString(R.string.xupdate_error_check_apk_cache_dir_empty));

        this.eMessages.append(Error.PROMPT_UNKNOWN, this.context.getString(R.string.xupdate_error_prompt_unknown));
        this.eMessages.append(Error.PROMPT_ACTIVITY_DESTROY, this.context.getString(R.string.xupdate_error_prompt_activity_destroy));

        this.eMessages.append(Error.DOWNLOAD_FAILED, this.context.getString(R.string.xupdate_error_download_failed));
        this.eMessages.append(Error.DOWNLOAD_PERMISSION_DENIED, this.context.getString(R.string.xupdate_error_download_permission_denied));

        this.eMessages.append(Error.INSTALL_FAILED, this.context.getString(R.string.xupdate_error_install_failed));
    }

    public Context context() { return this.context; }

    public IFileEncryptor encryptor() { return this.mFileEncryptor; }

    public Map<String, Object> params() { return this.mParams; }

    public SparseArray<String> errorMessages() { return this.eMessages; }

    public static XUpdate with() {
        if (mInstance == null) {
            synchronized (XUpdate.class) {
                if (mInstance == null) {
                    throw new IllegalArgumentException(("XUpdate must be initialize."));
                }
            }
        }
        return mInstance;
    }

    public static void init(@NonNull XUpdate xUpdate) {
        if (mInstance != null) {
            throw new IllegalArgumentException(("XUpdate is already initialized, do not initialize again."));
        }

        synchronized (XUpdate.class) {
            if (mInstance != null) {
                throw new IllegalArgumentException(("XUpdate is already initialized, do not initialize again."));
            }
            mInstance = xUpdate;
        }
    }

    /**
     * 保存忽略的版本信息
     * @param version 版本
     */
    public void saveIgnoreVersion(String version) {
        SPUtils.getInstance(SP_NAME).put(IGNORE_VERSION, version);
    }

    /**
     * 是否是忽略版本
     * @param version 新版本
     * @return 是否是忽略版本
     */
    public boolean isIgnoreVersion(String version) {
        return SPUtils.getInstance(SP_NAME).getString(IGNORE_VERSION, "").equals(version);
    }

    /**
     * 开始安装apk文件
     * @param context        传activity可以获取安装的返回值
     * @param entity        更新信息
     * @return
     */
    public boolean startInstallApk(@NonNull Context context, @NonNull LibraryUpdateEntity entity) {
        File apkFile = new File(entity.getFileDir(), entity.getFileName());
        if (!this.mFileEncryptor.isFileValid(entity)) {
            /* 文件校验无效 */
            this.onUpdateError(new UpdateError(Error.INSTALL_FAILED, "Apk file verify failed, please check whether the encrypt value you set is correct！"));
            FileUtils.delete(apkFile);
            return Boolean.FALSE;
        }

        XLog.tag(LOG_TAG).d("开始安装apk文件, 文件路径:" + apkFile.getAbsolutePath() + ", 下载信息:" + entity);

        if (this.mInstaller.startInstall(context, apkFile)) {
            return Boolean.TRUE;
        } else {
            onUpdateError(new UpdateError(Error.INSTALL_FAILED));
            return Boolean.FALSE;
        }
    }

    /**
     * 异常回调
     * @param updateError 异常
     */
    public void onUpdateError(@NonNull UpdateError updateError) {
        this.mOnUpdateFailureListener.onFailure(updateError);
    }

    /**
     * 构造器
     */
    public static class Builder {

        private final Application context;

        /* 请求参数 */
        private Map<String, Object> params;

        /* 文件加密器 */
        private IFileEncryptor fileEncryptor;

        private IUpdateInstaller installer;
        private OnUpdateFailureListener failureListener;

        public Builder(@NonNull Context context) {
            this((Application) context.getApplicationContext());
        }

        public Builder(@NonNull final Application application) {
            this.context = application;
            this.params = new TreeMap<>();

            this.fileEncryptor = new DefaultFileEncryptor();

            this.installer = new DefaultIUpdateInstaller();
            this.failureListener = new DefaultUpdateFailureListener();
        }

        /**
         * 设置全局的apk更新请求参数
         * @param key   键
         * @param value 值
         * @return this
         */
        @NonNull
        public Builder param(@NonNull String key, @NonNull Object value) {
            XLog.tag(LOG_TAG).d("设置全局参数, key:" + key + ", value:" + value);
            this.params.put(key, value);
            return this;
        }

        /**
         * 设置全局的apk更新请求参数
         * @param params apk更新请求参数
         * @return this
         */
        @NonNull
        public Builder params(@NonNull Map<String, Object> params) {
            StringBuilder sb = new StringBuilder("设置全局参数:{\n");
            for (Map.Entry<String, Object> entry : params.entrySet()) {
                sb.append("key = ")
                        .append(entry.getKey())
                        .append(", value = ")
                        .append(entry.getValue().toString())
                        .append("\n");
            }
            sb.append("}");
            XLog.tag(LOG_TAG).d(sb.toString());

            this.params = params;
            return this;
        }

        /**
         * 设置文件加密器
         * @param ec 加密器
         * @return
         */
        @NonNull
        public Builder encryptor(@NonNull IFileEncryptor ec) {
            this.fileEncryptor = ec;
            return this;
        }

        /**
         * 设置安装器
         * @param installer
         * @return
         */
        @NonNull
        public Builder setInstaller(@NonNull IUpdateInstaller installer) {
            this.installer = installer;
            return this;
        }

        /**
         * 设置失败监听
         * @param listener
         * @return
         */
        @NonNull
        public Builder setFailureListener(@NonNull OnUpdateFailureListener listener) {
            this.failureListener = listener;
            return this;
        }

        public XUpdate build() {
            return new XUpdate(this.context, this.params, this.fileEncryptor, this.installer, this.failureListener);
        }

    }
}
